feat(client)!: add Docker Hub auth sub-client - #618
Open
Benehiko wants to merge 1 commit into
Open
Conversation
Benehiko
force-pushed
the
feat/client-dockerhub-helpers
branch
from
August 21, 2026 11:26
785b066 to
12bdd57
Compare
Add a client/dockerhub package that reads Docker Hub access tokens and account profiles from the secrets engine and decodes them into typed values, so consumers no longer need to know the realm layout or the JSON payload format. The Client interface gains a HubAuth(...dockerhub.Option) accessor returning the dockerhub.ClientAuth sub-client; dockerhub.New wires the same sub-client over any bare secrets.Resolver. dockerhub.Staging() switches the lookups to the Docker Hub staging realms. ClientAuth resolves the default signed-in account through the profile metadata realm (docker/auth/metadata/hub/default), fetches a specific account under docker/auth/hub/<username>, and lists all signed-in profiles. Usernames and stored user ids are validated to name exactly one account entry inside the accounts realm, so a tampered profile or crafted username cannot address another secret. Claim decoding is dependency-free: NumericDate accepts integer, fractional, and exponent epochs and marshals whole seconds (matching golang-jwt/jwt v5 defaults), and Audience accepts a single string or an array. BREAKING CHANGE: the Client interface gains a HubAuth method; implementations must add it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Benehiko
force-pushed
the
feat/client-dockerhub-helpers
branch
from
August 21, 2026 15:25
a8c778e to
a05fcc7
Compare
Benehiko
marked this pull request as ready for review
August 21, 2026 15:28
docker-agent
left a comment
Contributor
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
This PR introduces a well-structured typed sub-client for Docker Hub authentication. The security design (realm pinning, username validation, default-session guard) is sound overall. One correctness bug was confirmed in the nil-envelope-ID path of ListProfiles, and a best-effort error-suppression behaviour is undocumented.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
client/dockerhub: a typed sub-client for reading Docker Hub authentication state (access tokens and account profiles) from the secrets engine.Why
Fetching a Hub token today means knowing the realm layout (
docker/auth/hub/**,docker/auth/metadata/hub/**) and the raw JSON payload format. This PR hides both behind a small typed API.API
client.Clientgains an accessor (kubernetes-clientset style);dockerhub.Newwires the same sub-client over any baresecrets.Resolver:GetDefaultSession(ctx)GetSession(ctx, username)GetDefaultProfile(ctx)ListProfiles(ctx)A
UserSessioncarries the raw JWT (AccessToken) plus decodedClaims— typedNumericDateandAudience, no JWT library dependency.Errors:
ErrNoSession(no stored credential) andErrNoDefaultProfile(no default account set). The latter wraps the former, so callers that only care about having a usable session check one sentinel.Safety
GetSessionrejects usernames containing/or wildcards, so a username can only ever address one account entry.GetDefaultSessionvalidates that the storeduser_idnames exactly one account entry inside the accounts realm (docker/auth/hub/*), so a tampered profile cannot redirect the lookup to another secret.Also in this PR
docker/auth/metadata/hub/**corrected: it holds one profile entry per signed-in account plus adefaultentry duplicating the default account's profile (previously described as a single default-user pointer).Breaking: the
Clientinterface gains aHubAuthmethod — implementations must add it.🤖 Generated with Claude Code